/**
 * @file      tcp_server.c
 * @author    wzzlyzdn (wzzlyzdn@163.com)
 * @brief     TCP 协议服务端函数封装
 * @version   0.1
 * @date      2022-08-04
 *
 * @copyright Copyright (c) 2022 wzzlyzdn
 *
 * @note      历史记录:
 *            -
 * @warning
 * @par       修改记录:
 * <table>
 * <tr><th>date          <th>Version    <th>Author      <th>Description    </tr>
 * <tr><td>2022-08-04    <td> 0.1       <td>wzzlyzdn       <td>创建初始版本    </tr>
 * <tr><td>2022-08-12    <td> 0.2       <td>wzzlyzdn       <td>增加文件注释    </tr>
 * <tr><td>2022-08-15    <td> 0.3       <td>wzzlyzdn       <td>校验入参，返回值 </tr>
 * </table>
 */
#include "server_client.h"
#include "server.h"
extern int file_transfer_state;
static int thread_exit_code = SERVER_RUNNING;

#define CLIENT_NUM 10

CLIENT_INFO client[CLIENT_NUM] = {0};

pthread_t thread_recv[CLIENT_NUM];
pthread_t thread_send[CLIENT_NUM];

static FILE_INFO filesend;//

/**
 * @fn        void *server_recv(void *arg)
 * @brief     线程函数 - 服务器接收信息
 *
 * @param     [in] arg       CLIENT_INFO 结构体数组索引值，用于寻找对应客户端 sockfd 等信息
 *
 * @return    void*
 */
void *server_recv(void *arg)
{
    int num = (int)((long)arg);
    if (num < 0 || num >= CLIENT_NUM)
    {
        DEBUG_INFO("err : arg\n");
        thread_exit_code = 1;
    }

    int sockfd_server = client[num].sockfd_client;
    if (sockfd_server < 0)
    {
        DEBUG_INFO("err : sockfd_server 错误\n");
        thread_exit_code = 1;
    }

    int ret_recv = 0;
    char retbuf[BUFFER_SIZE + 1] = {0};

    while (1 && !thread_exit_code)
    {
        ret_recv = recv(sockfd_server, retbuf, sizeof(retbuf), 0);
        if (ret_recv <= 0)
        {
            DEBUG_INFO("err : recv, return %d\n", ret_recv);
            thread_exit_code = 1;
        }

        retbuf[ret_recv] = '\0';
        DEBUG("客户端：%s\n", retbuf);

        if (!strncmp(retbuf, "quit", sizeof("quit")) || !strncmp(retbuf, "QUIT", sizeof("QUIT")) || !strncmp(retbuf, "退出", sizeof("退出")))
        {
            DEBUG("退出\n");
            thread_exit_code = CLIENT_EXIT;
            DEBUG_INFO("server_recv 函数 thread_exit_code 置为 -1 实际为 %d\n", thread_exit_code);
            break;
        }

        if (!strncmp(retbuf, "$FILE_TRANSFER_START$", sizeof("$FILE_TRANSFER_START$")))
        {
            DEBUG_INFO("file_transfor_flag = FILE_TRANSFER_START\n");
            // file_transfor_flag = FILE_TRANSFER_START;
            // file_receive_flag = 1;
        }

        /* 断点续传请求 */
        if (!strncmp(retbuf, "$FILE_TRANSFER_CONTINUE$", sizeof("$FILE_TRANSFER_CONTINUE$")))
        {
            DEBUG_INFO("文件传输继续\n");
            file_transfer_state = FILE_TRANSFER_CONTINUE;
        }

        /* 文件传输请求 */
        if (!strncmp(retbuf, "$FILE_TRANSFER$", sizeof("$FILE_TRANSFER$")))
        {
            file_info_bzero_except_ip(&filesend);
            ret_recv = recv(sockfd_server, retbuf, sizeof(retbuf), 0);
            retbuf[ret_recv] = '\0';
            DEBUG_INFO("%s\n", retbuf);
            data_packets_parsing(&filesend, retbuf);
            filesend.port = 19202;
            data_packets_combining(filesend, retbuf);
            display_data_packets(filesend);
            send(sockfd_server, "$FILE_TRANSFER_REQUEST_ALLOW$", sizeof("$FILE_TRANSFER_REQUEST_ALLOW$"), 0);
            usleep(200 * 1000);
            send(sockfd_server, retbuf, strlen(retbuf), 0);
            DEBUG_INFO("%s\n", retbuf);
            filesend.sockfd = socket_create(19202);

            pthread_t thread_file_send;
            pthread_create(&thread_file_send, NULL, file_transform, (void *)&filesend);
        }

        /* 请求文件列表 */
        if (!strncmp(retbuf, "$FILE_LIST_REQUEST$", sizeof("$FILE_LIST_REQUEST$")))
        {
            DEBUG_INFO("display_flie_list\n");
            display_flie_list(retbuf, sockfd_server);
        }

        /* 文件中断请求 */
        if (!strncmp(retbuf, "$FILE_TRANSFER_BREAK$", sizeof("$FILE_TRANSFER_BREAK$")))
        {
            DEBUG_INFO("文件传输中断 file_transfer_state= %d\n", file_transfer_state);
            file_transfer_state = FILE_TRANSFER_BREAK;
            DEBUG_INFO("file_transfer_state= %d\n", file_transfer_state);
        }
    }

    DEBUG_INFO("server_recv 线程退出\n");
    pthread_exit(NULL);
}

/**
 * @fn        void *server_send(void *arg)
 * @brief     线程函数 - 服务器发送消息
 *
 * @param     [in] arg       CLIENT_INFO 结构体数组索引值，用于寻找对应客户端 sockfd 等信息
 *
 * @return    void*
 */
void *server_send(void *arg)
{
    int num = (int)((long)arg);
    if (num < 0 || num >= CLIENT_NUM)
    {
        DEBUG_INFO("err : arg\n");
        thread_exit_code = 1;
    }

    int sockfd_server = client[num].sockfd_client;
    if (sockfd_server < 0)
    {
        DEBUG_INFO("err : sockfd_server 错误\n");
        thread_exit_code = 1;
    }

    char sendbuf[BUFFER_SIZE + 1] = {'\0', '\0'};
    int ret_send = 0;

    while (1 && !thread_exit_code)
    {
        /* 不安全函数 */
        scanf("%s", sendbuf);
        /* 此处 sizeof(sendbuf) 非常有问题 */
        sendbuf[sizeof(sendbuf)] = '\0';

        ret_send = send(sockfd_server, sendbuf, strlen(sendbuf), 0);
        if(ret_send <= 0)
        {
            DEBUG_INFO("err : send, return %d\n",ret_send);
            thread_exit_code = 1;
        }

        if (!strncmp(sendbuf, "quit", sizeof("quit")) || !strncmp(sendbuf, "QUIT", sizeof("QUIT")) || !strncmp(sendbuf, "退出", sizeof("退出")))
        {
            DEBUG("服务端退出\n");
            thread_exit_code = SERVER_MAINTENANCE;
            DEBUG_INFO("server_send 函数 thread_exit_code 置为 1 实际为 %d\n", thread_exit_code);
            break;
        }

        /* 文件传输请求 */
        if (!strncmp(sendbuf, "$FILE_TRANSFER$", sizeof("$FILE_TRANSFER$")))
        {
            filesend.port = 19202;
            DEBUG("输入文件名\n");
            scanf("%s", filesend.filename);
            data_packets_combining(filesend, sendbuf);
            send(sockfd_server, sendbuf, strlen(sendbuf), 0);
            DEBUG_INFO("%s\n", sendbuf);
            filesend.sockfd = socket_create(19202);

            pthread_t thread_file_send;
            pthread_create(&thread_file_send, NULL, file_transform, (void *)&filesend);

            DEBUG_INFO("线程退出位置\n");
        }
    }

    DEBUG_INFO("server_send 线程退出\n");
    pthread_exit(NULL);
}


/**
 * @fn        int servet_init()
 * @brief     服务端初始化 即 socket 创建 绑定 监听
 * 
 * @return    int            失败返回错误代码 SOCKET_CREATE_ERR, BIND_ERR, LISTEN_ERR;
 *                           成功返回 socket() 函数返回的 sockfd;
 */
int servet_init()
{
    /* 数据报 SOCK_DGRAM 流 SOCK_STREAM */
    /* 创建 Socket */
    DEBUG("/* 创建 Socket */\n");
    int reuse = 1;
    int sockfd_server = -1;
    int ret_bind = -1;
    int ret_listen = -1;

    sockfd_server = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd_server)
    {
        DEBUG_INFO("err : 创建 Socket 失败!\n");
        return SOCKET_CREATE_ERR;
    }

    /* 绑定 bing */
    DEBUG("/* 绑定 bing */\n");
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    /* 此处代码作用：关闭 socket 之后，端口资源立即释放而不是进入 TIME_WAIT */

    setsockopt(sockfd_server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    ret_bind = bind(sockfd_server, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if (-1 == ret_bind)
    {
        DEBUG_INFO("err : 绑定端口失败!\n");
        return BIND_ERR;
    }

    /* 监听 listen */
    DEBUG("/* 监听 listen */\n");
    ret_listen = listen(sockfd_server, 5);
    if (-1 == ret_listen)
    {
        DEBUG_INFO("err : 监听失败!\n");
        return LISTEN_ERR;
    }
    return sockfd_server;
}

/**
 * @fn        int accept_client(int sockfd_server)
 * @brief     连接客户端
 * 
 * @param     [in] sockfd_server 服务器的 sockfd
 * 
 * @return    int            失败返回错误代码 ACCEPT_ERR;
 *                           成功返回 accept 返回值 客户端 sockfd
 */
int accept_client(int sockfd_server)
{
    struct sockaddr_in client_addr;
    bzero(&client_addr, sizeof(struct sockaddr_in));

    socklen_t socket_len = sizeof(client_addr);
    int sockfd_client = accept(sockfd_server, (struct sockaddr *)&client_addr, &socket_len);
    if (-1 == sockfd_client)
    {
        DEBUG_INFO("err : accept 失败!\n");
        return ACCEPT_ERR;
    }
    for (int i = 0; i < CLIENT_NUM; i++)
    {
        if (0 == client[i].sockfd_client)
        {
            client[i].sockfd_client = sockfd_client;
            thread_exit_code = 0;
            pthread_create(thread_recv + i, NULL, server_recv, (void *)((long)i));
            pthread_detach(thread_recv[i]);
            pthread_create(thread_send + i, NULL, server_send, (void *)((long)i));
            pthread_detach(thread_send[i]);
            break;
        }
    }
    DEBUG("接入客户机%s\n", inet_ntoa(client_addr.sin_addr));
    return sockfd_client;
}

/**
 * @fn        void *server(void *sockfd)
 * @brief     线程函数 - 用于不断 accept 客户端请求
 * 
 * @param     [in] sockfd    服务器 sockfd
 * 
 * @return    void*          
 */
void *server(void *sockfd)
{
    int sockfd_server = (int)((long)sockfd);
    while (1)
    {
        if (ACCEPT_ERR == accept_client(sockfd_server))
        {
            DEBUG_INFO("err : accept_client error code %d\n", ACCEPT_ERR);
            break;
        }
    }
    thread_exit_code = -1;
    DEBUG_INFO("server 线程退出\n");
    pthread_exit(NULL);
}

/**
 * @fn        int tcp_server_main(int argc, char *argv[])
 * @brief     TCP 服务端函数 用于处理错误代码，以及程序资源释放
 * 
 * @param     [in] argc      主函数 argc
 * @param     [in] argv      主函数 argv 外部参数
 * 
 * @return    int            错误返回 相应错误码；正确返回 0；
 */
int tcp_server_main(int argc, char *argv[])
{
    DEBUG("---————————————————=☆=————————————————---\n");

    int sockfd_server = servet_init();

    bzero(&filesend, sizeof(filesend));
    /* 判断返回代码 */
    switch (sockfd_server)
    {
    case SOCKET_CREATE_ERR:
        DEBUG_INFO("err : error code %d\n", SOCKET_CREATE_ERR);
        return SOCKET_CREATE_ERR;
        break;
    case BIND_ERR:
        DEBUG_INFO("err : error code %d\n", BIND_ERR);
        close(sockfd_server);
        return BIND_ERR;
        break;
    case LISTEN_ERR:
        DEBUG_INFO("err : error code %d\n", LISTEN_ERR);
        close(sockfd_server);
        return LISTEN_ERR;
        break;
    case ACCEPT_ERR:
        DEBUG_INFO("err : error code %d\n", ACCEPT_ERR);
        close(sockfd_server);
        return ACCEPT_ERR;
        break;

    default:
        break;
    }

    pthread_t thread_server;
    pthread_create(&thread_server, NULL, server, (void *)((long)sockfd_server));
    pthread_detach(thread_server);

    /* 等待客户端连接 */
    DEBUG("/* 等待客户端连接 */\n");
    while (1)
    {
        if (SERVER_MAINTENANCE == thread_exit_code)
        {
            DEBUG("服务端维护 退出代码 %d\n", SERVER_MAINTENANCE);
            break;
        }
        if (CLIENT_EXIT == thread_exit_code)
        {
            DEBUG("客户端主动退出 退出代码 %d\n", CLIENT_EXIT);
            break;
        }
    }

    /* 关闭 Socket */
    for (int i = 0; i < CLIENT_NUM; i++)
    {
        if (client[i].sockfd_client)
        {
            close(client[i].sockfd_client);
            memset(&client[i], 0, sizeof(client[i]));
        }
    }

    DEBUG("/* 关闭 Socket */\n");
    close(sockfd_server);
    DEBUG("---————————————————=☆=————————————————---\n");
    return 0;
}